iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0

在上一篇,我們已經完成了 Vitest 的初始化,並撰寫了第一個單元測試。但光有測試檔還不夠,我們也要透過覆蓋率來檢視測試是否完整,避免有些功能被遺漏沒檢查到。
本篇將會延續上一篇的基礎,帶你進一步加入測試覆蓋率,並撰寫一個簡單的整合測試,讓驗收過程更全面😀。

本篇重點整理:

  • 測試覆蓋率:簡單了解覆蓋率並實裝作測試
  • 整合測試:延續上一篇,製作更進階一點的範例,並介紹所用到的程式碼

測試覆蓋率

在寫測試的時候,我們常常會有一個疑問:

到底我的測試有沒有把程式的所有情況都跑過❓

這時候就需要測試覆蓋率 ( Test Coverage ),就是到底測試覆蓋了多少程式碼。
這能幫助我們發現:

  • 哪些邏輯沒被測試到?
  • 哪些「潛在 bug」隱藏在沒跑過的地方?
  • 測試是不是僅僅測 happy-path,而忽略錯誤處理?

常見的覆蓋率指標

當我們用 Vitest 跑覆蓋率時,通常會看到這幾個指標:

  • Statements ( 陳述句覆蓋率 )
    每一行程式碼是否有被執行到。
  • Branches (分支覆蓋率)
    條件判斷是否都有測過( 例如 if / else )。
  • Functions (函式覆蓋率)
    每一個函式是否都有被呼叫。
  • Lines (行數覆蓋率)
    具體的行數是否被執行到( 與 statements 類似,但更精細 )。

覆蓋率越高越好嗎?

覆蓋率只是參考指標,不是唯一標準。100% 覆蓋率 ≠ 沒有 bug,因為測試可能只覆蓋程式碼而沒驗證正確性。實務上大部分專案會設定 70% ~ 90% 作為目標

建議做法

  • CI/CD 加入 coverage:在 PR / merge 前自動檢查覆蓋率,避免測試不足。
  • 設定最低門檻:在 vitest.config.ts 裡可以限制測試覆蓋率,例如:
coverage: {
  lines: 80,
  functions: 80,
  branches: 70,
  statements: 80,
}

這樣如果覆蓋率低於標準,測試就會失敗。


操作流程

一樣,先安裝 Vitest Coverage 套件:

npm i -D @vitest/coverage-v8

vitest.config.ts 裡加上 coverage 設定:

import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    {/* ... */}
    coverage: {
      provider: "v8", // 使用 v8 引擎 (官方推薦)
      reporter: ["text", "json", "html"], 
      reportsDirectory: "./coverage", // 輸出到 coverage 資料夾
    },
  },
});

package.json 加一個 script:

"scripts": {
  "test": "vitest",
  "coverage": "vitest run --coverage"
}

執行測試

npm run coverage

接著可以看到終端機與檔案裡會顯示 coverage 的結果了👍。


⚜️整合測試

上一篇 有介紹到整合測試,這裡就不多做贅述,這裡再提供更進階一點的範例,這些會是整合測試經常會遇到的程式碼:

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { vi } from "vitest";
import LoginForm from "../LoginForm";

describe("LoginForm 整合測試", () => {
  it("使用者輸入帳密並送出後,應該呼叫 onSubmit", async () => {
    const mockSubmit = vi.fn(); // 建立假的函式來監控是否有被呼叫

    render(<LoginForm onSubmit={mockSubmit} />);

    // 模擬使用者輸入帳號與密碼
    await userEvent.type(screen.getByLabelText(/帳號/i), "testUser");
    await userEvent.type(screen.getByLabelText(/密碼/i), "123456");

    // 模擬按下送出按鈕
    await userEvent.click(screen.getByRole("button", { name: /登入/i }));

    // 驗證 submit 是否被呼叫,且帶入正確參數
    expect(mockSubmit).toHaveBeenCalledWith({
      username: "testUser",
      password: "123456",
    });
  });
});
  • render:把 React 元件渲染在測試環境裡,就像真的放到瀏覽器一樣,方便後續操作。
  • screen:提供一個「查詢 DOM 的全域物件」,常用 getByTextgetByRolegetByLabelText 來找到畫面元素。
  • userEvent:模擬使用者操作(輸入文字、點擊、鍵盤操作),比 fireEvent 更接近真實使用情境。
  • vi:來自 Vitest,負責建立 mock 函式模擬外部依賴,在測試中能精確監控呼叫次數、參數內容。

上一篇
屋況驗收[ 1 / 2 ]:Vitest 初始化 & 單元測試
下一篇
屋內安全[ 1 / 6 ]:Firebase Rules
系列文
不只是登入畫面!一起打造現代化登入系統12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言